const request = require('request');
const cheerio = require('cheerio');
const fs = require('fs');
const path = require("path");
const moment = require('moment');
const isUtf8 = require('is-utf8');
const icl = require('iconv-lite');

//百度搜索基础url
let baiduBaseUrl = 'http://m.baidu.com/from=844b/s';
let iphoneEmulation = 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1';
let androidEmulation = 'Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Version/8.0 Mobile Safari/537.36';
let $selectDevice = 1;

//显示输出文件路径
let outFilePath;
//错误日志文件路径
let errorLog;

let data;

function startSearch() {
    //清空"执行结束标识"
    isComplete = {};
    startWordNum = -1;// 清空  开始执行的词数
    endWordNum = 0;// 清空 执行结束的词数
    document.querySelector("#list").innerHTML = ''; //清空显示区域的内容
    document.querySelector('#ended').innerHTML = '0';//已完成的关键词数量清0

    //文件对象
    let file = document.querySelector('#files').files[0];
    if(!file){
        alert('请先选择文件');
        return;
    }
    //执行搜索按钮，设为不可用
    document.querySelector("#A").setAttribute('disabled','disabled');
    document.querySelector("#B").setAttribute('disabled','disabled');
    document.querySelector("#fileLink").setAttribute('disabled','disabled');
    document.querySelector("#numSelect").setAttribute('disabled','disabled');
    document.querySelector('body').setAttribute('disabled','disabled');
    document.querySelector("#startBtn").setAttribute('disabled','disabled');
    document.querySelector("#startBtn").style.backgroundColor='#9a9a9a';
    //设备选择不可用
    document.querySelector("#deviceSelect").setAttribute('disabled','disabled');

    //检查searchResult目录是否存在
    let outFileDir = path.dirname(file.path)+ path.sep+'searchResult';
    if(!fs.existsSync(outFileDir)){//如果不存在结果目录，则创建这个目录
        fs.mkdirSync(outFileDir);
    }
    //输出结果文件的路径
    let formatDate = moment(new Date()).format('YYYYMMDDHHmmss'); /*格式化时间*/
    outFilePath = outFileDir + path.sep + 'searchRes'+formatDate+'.csv';
    errorLog = outFileDir + path.sep + 'error'+formatDate+'.log';//错误日志路径

    //判断是否是utf8文件
    let res = fs.readFileSync(file.path);
    console.log(isUtf8(res));//  判断缓冲区是否是utf8编码 => true
    //设置读取时使用的字符集
    let strEncoding = isUtf8(res) === true? 'UTF-8': 'GBK';
    data = fs.readFileSync(file.path, {encoding:'binary'});
    //将独到的内容放在buffer中
    let buf = Buffer.from(data, 'binary');
    let decodedText = icl.decode(buf, strEncoding);

    let $keyWord_list = decodedText.split(/\r\n+/);//关键词列表，正则匹配(按换行分割)
    document.querySelector('#total').innerHTML = $keyWord_list.length+'';//显示总数
    bat($keyWord_list);//批量控制

}

/**
 * 批量控制
 * @param $keyWord_list
 */
function bat($keyWord_list) {
    let N = 5; //每次批量执行的个数(线程数)
    for (let i = 0; i < N; i++) {//循环执行一批(N个)数据
        bat2($keyWord_list, N);
    };
}

/**
 * 执行完成
 */
function complete() {
    //执行搜索按钮，设为可用
    document.querySelector("#A").removeAttribute('disabled');
    document.querySelector("#B").removeAttribute('disabled');
    document.querySelector("#fileLink").removeAttribute('disabled');
    document.querySelector("#numSelect").removeAttribute('disabled');
    document.querySelector("#startBtn").removeAttribute('disabled');
    document.querySelector("#startBtn").style.backgroundColor='#1E90FF';
    //设备选择可用
    document.querySelector("#deviceSelect").removeAttribute('disabled');
    alert('处理结束！结果保存在'+outFilePath);
}

let isComplete = {};//执行完所有线程的标识
let startWordNum = -1;// 开始执行的词数
let endWordNum = 0;// 执行结束的词数
function bat2($keyWord_list, N) {
    if(startWordNum - N >=0) {
        endWordNum += 1;
        document.querySelector('#ended').innerHTML = endWordNum+1;
    }
    startWordNum += 1;//执行结束的词数 +1
    if(startWordNum >= $keyWord_list.length) {
        //let i = 0;
        isComplete[startWordNum] = true; // index = 10,11,12
        for (let n = 0; n < N; n++) {
            if (!isComplete[$keyWord_list.length + n]) {
                return;
            }
        }
        complete();//结束回调
        return;
    }
    if ($keyWord_list[startWordNum] != null && $keyWord_list[startWordNum] != undefined && $keyWord_list[startWordNum] != '') {
        showInfo($keyWord_list[startWordNum],'start');// 页面显示提示信息：开始处理xx关键词
    }
    doRequest($keyWord_list[startWordNum], function () {
        bat2($keyWord_list, N);
    });//处理一个网络请求


}

/**
 * 处理一个网络请求,每个词必须找到20个对应结果
 * @param item 一个关键词
 * @param $keyWord_list 关键词数组
 * @param index
 */
function doRequest(oneWord, callback) {
    if (oneWord == null || oneWord == undefined || oneWord == '') {
        callback();//空字符串直接结束，执行回调 // 外层回调
        return;
    }

    //获取自然流量
    let retryCount = 0;
    const fun = function (page, total) {

        //处理一个词，每个词获取20个结果
        //word,page,row,callback,
        doRequestOne(oneWord, page, total, function (row) {
            if (row == -1){
                retryCount += 1;
                if(retryCount > 3){
                    //输出错误信息
                    witeFileFunc(oneWord + '，重试3次皆失败，请手动重试', 'err');
                    callback();
                    return;
                }
                //console.log('retry '+retryCount + '次');
                fun(page, total);//重试

            } else if (row < total) {
                retryCount = 0;
                if(page > 15){ //如果翻了15页还没有结果，就放弃了
                    callback(); // 外层回调
                    return;
                }
                fun(page + 1, total - row);//翻页
            } else {
                retryCount = 0;
                showInfo(oneWord,'end');//某个关键词处理结束
                callback(); // 外层回调
            }
        });
    };

    //获取广告
    const funOnlyAD = function(page, total){
        doRequestOne(oneWord, page, total, function (row) {
            if (row == -1){
                retryCount += 1;
                if(retryCount > 3){
                    //输出错误信息
                    witeFileFunc(oneWord + '，重试3次皆失败，请手动重试', 'err');
                    callback();
                    return;
                }
                funOnlyAD(page, total);//重试
            } else {
                retryCount = 0;
                showInfo(oneWord,'end');//某个关键词处理结束
                callback(); // 外层回调
            }
        });
    };
    //判断是要搜索哪种结果
    let searchType = document.querySelector("input[type='radio'][name='searType']:checked").value;
    //console.log(searchType);
    if(searchType==='0'){//搜广告
        funOnlyAD(0, '');
    } else { //搜自然流量
        //获取选中的，每个词需要获取结果的数量
        let totalNum = document.querySelector("#numSelect").value;
        fun(0,totalNum);
    }

}
/**
 * 处理一个网络请求(关键词搜索)
 * @param oneWord 一个关键词
 * @param page 搜索结果的第几页
 * @param total 一个关键词要搜索的内容个数
 * @param callback 回调函数
 */
function doRequestOne(oneWord, page, total, callback) {
    let pn = page * 10;//翻页
    // 百度搜索url地址: http://www.baidu.com/s?wd=中国翡翠公司&pn=1
    let url = `${baiduBaseUrl}?wd=${oneWord}&pn=${pn}`;
    //showInfo(oneWord,'start');// 页面显示提示信息：开始处理xx关键词

    //自定义请求头
    let options = {
        url: encodeURI(url),
        headers: {
            //$selectDevice 默认是1（iphone）,可以选择2（安卓）
            'User-Agent': $selectDevice===1? iphoneEmulation: androidEmulation
        },
        timeout: 3000 //超时时间
    };

    request(options, (function(oneWord){
        return function (error, response, body) {
            if(error){
                //输出错误信息
                witeFileFunc(oneWord + ',error: '+error, 'err');
                callback(-1);//网络请求失败，回调时重试
            } else {
                if(body){ // 分析响应结果 ,将结果写入到searchRes.txt文件中
                    let count = 0;
                        //判断是要搜索哪种结果
                    let searchType = document.querySelector("input[type='radio'][name='searType']:checked").value;
                    if(searchType==='0'){
                        count = writePhoneADRes(oneWord, body);
                    } else {
                        count = writePhoneResWithoutAD(oneWord, body);//将结果写入到文件，并返回沐足条件的结果数
                    }
                    callback(count);//网络请求结束，执行回调
                }else{
                    //console.log("##拿到的信息为false##")
                    witeFileFunc(oneWord + ",拿到的信息为false", 'err');
                    callback(0);//网络请求结束，执行回调
                }
            }

        }})(oneWord));
    
}

/**
 * 分析电脑版响应结果(获取广告结果)
 * 将结果写入到searchRes.txt文件中
 * @param item 关键词
 * @param body 响应的网页内容
 */
function writeRes(item, body){

    const $ = cheerio.load(body);
    let $content = $('#content_left > div');
    for (let i = 0; i < $content.length; i++) {
        let $info = $content[i];//拿到一条搜索结果
        //取到当前item的推广信息
        let $tuiguang = $($info).find('span[data-tuiguang]');

        //如果推广信息存在，则保存当前item
        if($tuiguang.length > 0){
            let $infoHref = $($info).find('a[data-landurl]').attr('data-landurl');
            let $textConten = `${item},${$infoHref}`; //将要写入文件中的路径结果
            //将url写入到文件
            witeFileFunc(GetUrlRelativePath($textConten), 'succ');

        }
    }

}

/**
 * 分析手机版响应结果(获取广告结果)
 * 将结果写入到searchRes.txt文件中
 * @param item 关键词
 * @param body 响应的网页内容
 */
function writePhoneADRes(item, body){
    //console.log(body);
    const $ = cheerio.load(body);
    //手机端广告链接地址: $('.ec_ad_results > div')[0].attribs['data-lp']
    let $content = $('.ec_ad_results > div');
    if($content==null || $content==='' || $content===undefined || $content==='undefined'){
        return;
    }
    for (let i = 0; i < $content.length; i++) {
        //手机端广告链接地址: $('.ec_ad_results > link')[0].namespace
        let $encodeHref = $content[i].attribs['data-lp'];//取到着陆页加密地址
        let $infoHref = unescape($encodeHref.replace(/%/g, '%25'));//解密着陆页地址
        if($infoHref==null || $infoHref=='' || $infoHref==undefined || $infoHref=='undefined'){
            continue;
        }
        //拿到一条推广信息的链接[0].namespace
        let $textConten = `${item},${$infoHref}`; //将要写入文件中的路径结果

        if($textConten.indexOf("https") != -1){//过滤https
            continue;
        }
        if($textConten.indexOf("baidu.com") != -1){//过滤百度
            continue;
        }
        //let $textContenGbk = icl.decode($textConten, 'gbk');//将内容转换为gbk
        //将url写入到文件
        witeFileFunc(GetUrlRelativePath($textConten), 'succ');
    }

}

/**
 * 分析手机版响应结果(获取非广告结果)
 * 将结果写入到searchRes.txt文件中
 * @param item 关键词
 * @param body 响应的网页内容
 */
function writePhoneResWithoutAD(item, body){
    const $ = cheerio.load(body);
    //手机端广告链接地址: $('.ec_ad_results > div')[0].attribs['data-lp']
    //let $content = $('.c-clk-recommend');
    let $content = $('.c-result');
    if($content==null || $content==='' || $content===undefined || $content==='undefined'){
        return;
    }

    let count = 0; //满足条件的结果数量
    for (let i = 0; i < $content.length; i++) {

        let $encodeHref = $content[i].attribs['data-log'];//取到着陆页加密地址
        if($encodeHref==null || $encodeHref=='' || $encodeHref==undefined || $encodeHref=='undefined'){
            continue;
        }
        //console.log($encodeHref);
        let dataLog = JSON.parse($encodeHref.replace(/'/g,'"'));
        let $tempInfoHref = dataLog.mu;
        if($tempInfoHref==null || $tempInfoHref=='' || $tempInfoHref==undefined || $tempInfoHref=='undefined'){
            continue;
        }
        let $infoHref = decodeURI($tempInfoHref.replace(/%/g, '%25'));//解密着陆页地址
        if($infoHref==null || $infoHref=='' || $infoHref==undefined || $infoHref=='undefined'){
            continue;
        }
        //拿到一条信息的链接
        let $textConten = `${item},${$infoHref}`; //将要写入文件中的路径结果

        if($textConten.indexOf("https") != -1){//过滤https
            continue;
        }
        if($textConten.indexOf("baidu.com") != -1){//过滤百度
            continue;
        }
        count = count + 1;//结果+1;
        //let $textContenGbk = icl.decode($textConten, 'gbk');//将内容转换为gbk
        //将url写入到文件
        witeFileFunc(GetUrlRelativePath($textConten), 'succ');
    }

    return count;

}


/**
 * 自定义的写文件方法，
 * 如果文件不存在，就创建，如果文件存在就添加内容
 * @param $infoHref 对应链接
 */
function witeFileFunc($infoHref, option){
    let outfile;
    option==='succ'? outfile = outFilePath: outfile = errorLog;
    let $infoHrefGbk = icl.encode('\r\n'+$infoHref, 'gbk');//将内容转换为gbk

    let existFile = fs.existsSync(outfile); // 同步判断文件是否存在
    if(!existFile){//文件不存在，创建一个文件并写入内容
        fs.writeFile(outfile, $infoHrefGbk, 'binary',function (err) {
            if (err) {
                return console.error(err);
            }
        })
    } else {//文件存在，追加一行内容
        fs.appendFile(outfile, $infoHrefGbk, 'binary',function (err) {
            if (err) {
                return console.error(err);
            }
        })
    }
}


/**
 * 截取url中不带参数的部分
 * @param url
 * @returns {string}
 * @constructor
 */
function GetUrlRelativePath(url) {
    // linkUrl = linkUrl.replace(/%/g, '%25');
    // //先解码
    // let url = decodeURI(linkUrl);
    //过滤?号
    if(url.indexOf("?") != -1){
        //url = url.split("?")[0];
        //截取从0到问号的字符串
        url = url.substring(0, url.indexOf('?'));
    }
    //过滤#号
    if(url.indexOf("#") != -1){
        //url = url.split("?")[0];
        //截取从0到问号的字符串
        url = url.substring(0, url.indexOf('#'));
    }
    return url;
}

/**
 * 界面提示
 */
function showInfo(item, option) {
    let $li = document.createElement('li');
    if(option==='start'){
        $li.innerText = `正在处理 【${item}】 关键词 ...`;
    } else {
        $li.innerText = `关键词 【${item}】  处理结束。`;
    }
    let $ul = document.querySelector("#list");
    let $liEle = document.querySelectorAll("li");
    if($liEle.length >= 200){
        $ul.removeChild($liEle[0]); //删除头部的li
    }
    $ul.appendChild($li);//向尾部添加一个li
    $ul.scrollTop = $ul.scrollHeight;
}



//界面加载完成
window.onload=function(){
    //文件选择框，变更监听
    document.querySelector("#files").addEventListener('change', fillText, false);
    //点击文本框时，让文件框被点击
    document.querySelector("#fileLink").addEventListener('click', selectFile, false);
    //点击开始执行按钮
    document.querySelector("#startBtn").addEventListener('click',startSearch, false);

    //选择框点击事件
    document.querySelector("#deviceSelect").addEventListener('change', selectDevice, false);

}

/**
 * 单选框点击事件
 */
function chooseTaskType(typeValue) {
    //console.log(typeValue);
    if(typeValue==0){//搜广告
        //隐藏掉选数字的模块
        //document.querySelector("#numRow").display = 'none';
        document.querySelector("#numRow").setAttribute('hidden','true');
    } else { //搜自然流量
        //显示选数字的模块
        document.querySelector("#numRow").removeAttribute('hidden');
    }

}

/**
 * 标签被点击时，让文件选择框被点击
 */
function selectFile() {
    document.querySelector("#files").click();
}

/**
 * 文件改变，显示内容到标签中
 */
function fillText() {
    let $fileLable = document.querySelector("#fileLink");
    let $file = document.querySelector("#files").files[0];
    $fileLable.value = $file.name;
    //console.log($file.name);
}

/**
 * 设备选择
 */
function selectDevice() {
    //1就是苹果手机，2就是安卓手机
    $selectDevice = this.value;

}